package org.jpropelleralt.box.impl;

import java.util.Iterator;
import java.util.NoSuchElementException;

import org.jpropelleralt.box.Box;
import org.jpropelleralt.box.BoxFeatures;
import org.jpropelleralt.multiverse.Multiverse;
import org.jpropelleralt.reaction.Reaction;
import org.jpropelleralt.universe.Universe;
import org.jpropelleralt.utils.impl.Counter;
import org.jpropelleralt.utils.impl.StrongCounter;
import org.jpropelleralt.view.View;

/**
 * Default implementation of {@link BoxFeatures}
 */
public class BoxFeaturesDefault implements BoxFeatures {

	private final Universe universe;
	private final Box box;
	private Counter<View> views = null;
	private Counter<Reaction> reactions = null;
	private StrongCounter<Reaction> registeredReactions = null;
	
	/**
	 * Create a new {@link BoxFeaturesDefault}
	 * @param box		The {@link Box} we are used by
	 * @param universe	The {@link Universe} we belong to
	 */
	public BoxFeaturesDefault(Box box, Universe universe) {
		this.box = box;
		this.universe = universe;
	}

	@Override
	public Universe universe() {
		return universe;
	}

	@Override
	public void addView(View view) {
		Multiverse.prepareViewChange(box);
		try {
			if (views == null) {
				views = new Counter<View>();
			}
			views.add(view);
		} finally {
			Multiverse.concludeViewChange(box);
		}
	}

	@Override
	public void removeView(View view) {
		Multiverse.prepareViewChange(box);
		try {
			if (views == null) {
				return;
			}
			views.remove(view);			
		} finally {
			Multiverse.concludeViewChange(box);
		}
	}
	
	@Override
	public void addReaction(Reaction reaction) {
		Multiverse.prepareReactionChange(box);
		try {
			if (reactions == null) {
				reactions = new Counter<Reaction>();
			}
			reactions.add(reaction);
		} finally {
			Multiverse.concludeReactionChange(box);
		}
	}

	@Override
	public void removeReaction(Reaction reaction) {
		Multiverse.prepareReactionChange(box);
		try {
			if (reactions == null) {
				return;
			}
			reactions.remove(reaction);
		} finally {
			Multiverse.concludeReactionChange(box);
		}
	}

	
	@Override
	public void registerReaction(Reaction reaction) {
		Multiverse.prepareReactionChange(box);
		try {
			if (registeredReactions == null) {
				registeredReactions = new StrongCounter<Reaction>();
			}
			registeredReactions.add(reaction);
		} finally {
			Multiverse.concludeReactionChange(box);
		}
	}

	@Override
	public void unregisterReaction(Reaction reaction) {
		Multiverse.prepareReactionChange(box);
		try {
			if (registeredReactions == null) {
				return;
			}
			registeredReactions.remove(reaction);
		} finally {
			Multiverse.concludeReactionChange(box);
		}
	}
	
	@Override
	public Iterable<Reaction> reactions() {
		if (reactions == null) {
			return noReactions; 
		} else {
			return reactions;
		}
	}

	@Override
	public Iterable<View> views() {
		if (views == null) {
			return noViews; 
		} else {
			return views;
		}
	}

	private static EmptyIterable<View> noViews = new EmptyIterable<View>();
	private static EmptyIterable<Reaction> noReactions = new EmptyIterable<Reaction>();
	private static class EmptyIterable<T> implements Iterable<T> {
		private final EmptyIterator<T> iterator = new EmptyIterator<T>();
		@Override
		public Iterator<T> iterator() {
			return iterator;
		}
	}
	private static class EmptyIterator<T> implements Iterator<T> {
		@Override
		public boolean hasNext() {
			return false;
		}
		@Override
		public T next() {
			throw new NoSuchElementException();
		}
		@Override
		public void remove() {
		}
	}
}
